package msgtunnel

import (
	"encoding/base32"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"sync"

	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
	"google.golang.org/protobuf/proto"
)

//接口,因为tcp,udp,ws读的方式都不一样
type ClientCall interface {
	handleMsgFromClient()
}

var clientRwLock sync.RWMutex
var clientMap map[string]ClientCall
var reqHandleFunc map[string]func(reqBuffer []byte) proto.Message

func init() {
	reqHandleFunc = make(map[string]func(reqBuffer []byte) proto.Message)
}
func Reg(reqName string, cbfunc func([]byte) proto.Message) {
	reqHandleFunc[reqName] = cbfunc
}

func addToClientManager(conn net.Conn) {
	clientRwLock.Lock()
	defer clientRwLock.Unlock()
	//将用户的远端地址转换成为base32，当作map的key用来一一对应
	user_identify := base32.StdEncoding.EncodeToString([]byte(conn.RemoteAddr().String()))
	clientMap[user_identify] = tcpClient{
		conn: conn,
	}
	logrus.Info("client:", user_identify, ", start handle conn.")
	go clientMap[user_identify].handleMsgFromClient()
	//单个客户端处理自己的请求,tcp,udp,ws的handle方式都不同
}

func addToWsClienManager(conn *websocket.Conn) {
	clientRwLock.Lock()
	defer clientRwLock.Unlock()
	//将用户的远端地址转换成为base32，当作map的key用来一一对应
	user_identify := base32.StdEncoding.EncodeToString([]byte(conn.RemoteAddr().String()))
	clientMap[user_identify] = wsClient{
		conn: conn,
	}
	logrus.Info("ws client:", user_identify, ", start handle conn.")
	clientMap[user_identify].handleMsgFromClient()
	//单个客户端处理自己的请求,tcp,udp,ws的handle方式都不同
	//ws的不用go出协程，因为上层http已经go出协程了，这里需要阻塞对单个客户端的处理
}

func init() {
	clientMap = make(map[string]ClientCall)
}

// udp服务器实现
type udpSvr struct {
	listener net.Listener
}

func (s *udpSvr) listen(port int) {
	s.listener, _ = net.Listen("udp", fmt.Sprintf("0.0.0.0:%d", port))
	logrus.Infoln("start listen udp in :", port)
}
func (s *udpSvr) accept() {
	for {
		conn, err := s.listener.Accept()
		if err != nil {
			logrus.Println("udp accept error:", err.Error())
			continue
		}
		logrus.Println(conn.RemoteAddr().String(), " udp connected!")
		go s.handle(conn)
	}
}
func (s *udpSvr) handle(conn net.Conn) {
	addToClientManager(conn)
}

// tcp服务器实现
type tcpSvr struct {
	listener net.Listener
}

func (s *tcpSvr) listen(port int) {
	s.listener, _ = net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
	logrus.Infoln("start listen tcp in :", port)
}
func (s *tcpSvr) accept() {
	for {
		conn, err := s.listener.Accept()
		if err != nil {
			logrus.Println("tcp accept error:", err.Error())
			continue
		}
		logrus.Println(conn.RemoteAddr().String(), " tcp connected!")
		go s.handle(conn)
	}
}
func (s *tcpSvr) handle(conn net.Conn) {
	addToClientManager(conn)
}

//ws服务器实现
type wsSvr struct {
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  4096,
	WriteBufferSize: 4096,
	// 解决跨域问题
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
} // use default options
func (s *wsSvr) handleProto(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer conn.Close()
	addToWsClienManager(conn)
}

func (s *wsSvr) listenAndAccept(port int) {
	http.HandleFunc("/proto", s.handleProto)
	logrus.Infoln("start listen ws tcp in :", port)
	err := http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), nil)
	if err != nil {
		logrus.Errorln("wssocket listen in ", port, "err! ", err.Error())
		os.Exit(-1)
	}
}

type Server struct {
	udpServer udpSvr
	tcpServer tcpSvr
	wsServer  wsSvr
}

func (s Server) Listen(port int) {
	s.udpServer.listen(port)
	s.tcpServer.listen(port)
	go s.wsServer.listenAndAccept(port + 1)
	//go s.udpServer.accept()
	s.tcpServer.accept() //利用这个阻塞
}
